package be.nepherte.movmanager.client.gui.components;

import be.nepherte.movmanager.client.gui.components.Tab.TabInfo;

import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Hyperlink;
import com.google.gwt.user.client.ui.Widget;
import java.util.ArrayList;

/**
 * This class represents a list of Tabs.
 * <p>
 * This file is part of MovManager.
 * </p>
 * <p>
 * MovManager is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * </p>
 * <p>
 * MovManager is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * </p>
 * <p>
 * You should have received a copy of the GNU General Public License
 * along with MovManager.  If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses</a>.
 * </p>
 * 
 * @author	Bart Verhoeven, Google Inc.
 * @since	13/09/2009
 * @version 0.1
 */
public class TabList extends Composite {

	/**
	 * A hyper link class with a mouse over and mouse out effect.
	 */
	private class MouseLink extends Hyperlink {
		/**The index of the tab linked to this class.*/
		private int index;

		/**
		 * The constructor for MouseLink. Create the hyper link with the mouse hover effect.
		 * 
		 * @param	name
		 * 			name/text of the hyper link
		 * @param	index
		 * 			the index of the tab associated with this mouse link
		 */
		public MouseLink(String name, int index) {
			super(name, name);
			this.index = index;
			sinkEvents(Event.MOUSEEVENTS);
		}
		
		public int getIndex() {
			return index;
		}

		/**
		 * The actions to be performed on a browser event. Only 2 events are different from the normal on browser
		 * event of HyperLink: the mouse over and mouse out effect.
		 */
		public void onBrowserEvent(Event event) {
			switch (DOM.eventGetType(event)) {
			case Event.ONMOUSEOVER:
				mouseOver(index);
				break;

			case Event.ONMOUSEOUT:
				mouseOut(index);
				break;
			}

			super.onBrowserEvent(event);
		}
	}

	/** The horizontal panel where all the tabs are placed upon. */
	private HorizontalPanel list = new HorizontalPanel();
	/** A list with all the tabs it contains. */
	private ArrayList<TabInfo> tabs = new ArrayList<TabInfo>();
	/** The index of the currently selected tab. */
	private int selectedTab = -1;

	/**
	 * Create a new tab list. 			
	 */
	public TabList(Tab.Images images) {
		initWidget(list);
		list.add(images.gwtLogo().createImage());
		setStyleName("ks-List");
	}

	/**
	 * Add a tab to the list of tabs.
	 * 
	 * @param	info
	 * 			the info of the tab that should be added 
	 */
	public void addTab(final TabInfo info) {
		String name = info.getName();
		int index = list.getWidgetCount() - 1;

		MouseLink link = new MouseLink(name, index);
		list.add(link);
		tabs.add(info);

		list.setCellVerticalAlignment(link, HorizontalPanel.ALIGN_BOTTOM);
		styleTab(index, false);
	}
	
	/**
	 * Remove a tab from the list of tabs.
	 * 
	 * @param	info
	 * 			the info of the tab that should be removed
	 */
	public void removeTab(final TabInfo info) {
		int index = tabs.indexOf(info);
		
		for (Widget w: list) {
			if (w instanceof MouseLink) {
				MouseLink link = (MouseLink) w;
			
				if (link.getIndex() == index) {
					tabs.remove(info);
					list.remove(link);
					return;
				}
			}
		}
	}

	/**
	 * Find a tab by its name. When this tab list doesn't have a tab with the given name, it will return null.
	 * 
	 * @param	tabName
	 * 			the name of the tab you are looking for.
	 * @return	the tab info of the tab with the given name or null if the tab is not found
	 */
	public TabInfo find(String tabName) {
		for (int i = 0; i < tabs.size(); ++i) {
			TabInfo info = (TabInfo) tabs.get(i);
			
			if (info.getName().equals(tabName))
				return info;
		}

		return null;
	}

	/**
	 * Set a tab as selected. This includes setting the style of a selected tab. 
	 * 
	 * @param	name
	 * 			the name of the tab to be selected
	 */
	public void setTabSelection(String name) {
		if (selectedTab != -1)
			styleTab(selectedTab, false);

		for (int i = 0; i < tabs.size(); ++i) {
			TabInfo info = (TabInfo) tabs.get(i);
			if (info.getName().equals(name)) {
				selectedTab = i;
				styleTab(selectedTab, true);
				return;
			}
		}
	}

	/**
	 * Color a tab. If the tab is selected, it will get its color as specified in its TabInfo. If the tab is not selected,
	 * it will get the default light gray color.
	 * 
	 * @param	index
	 * 			the index the tab that should be colored
	 * @param	on
	 * 			whether the tab is active or not
	 */
	private void colorTab(int index, boolean on) {
		String color = "";
		if (on)
			color = ((TabInfo) tabs.get(index)).getColor();

		Widget w = list.getWidget(index + 1);
		DOM.setStyleAttribute(w.getElement(), "backgroundColor", color);
	}

	/**
	 * A mouse out effect that changes the color of a tab when you remove the mouse over it. The color will either be
	 * the tab color as specified in TabInfo if it is a selected tab or the default light gray background for unselected tabs.
	 * 
	 * @param	index
	 * 			the index of the tab that should get the mouse out effect
	 */
	private void mouseOut(int index) {
		if (index != selectedTab) 
			colorTab(index, false);
	}

	/**
	 * A mouse over effect that changes the color of a tab when you hover over it. The color will be the tab color as
	 * specified in TabInfo. 
	 * 
	 * @param	index
	 * 			the index of the tab that should get the mouse over effect
	 */
	private void mouseOver(int index) {
		if (index != selectedTab) 
			colorTab(index, true);
	}

	/**
	 * Apply a style to a tab. The style applied is ks-FirstSinkItem or ks-SinkItem depending on whether the tab is the first item or not.
	 * When the tab is selected, it gets either ks-FirstSinkItem-selected or ks-SinkItem-selected. These styles are defined in style.css.
	 * 
	 * @param	index
	 * 			the index of the tab that should get the style
	 * @param 	selected
	 * 			whether the tab is selected or not.
	 */
	private void styleTab(int index, boolean selected) {
		String style = (index == 0) ? "ks-FirstSinkItem" : "ks-SinkItem";
		
		if (selected) 
			style += "-selected";

		Widget w = list.getWidget(index + 1);
		w.setStyleName(style);
		colorTab(index, selected);
	}

	/**
	 * Set a tab visible.
	 * 
	 * @param	tab
	 * 			the info of the tab that should be made visible
	 * @param	visible
	 * 			whether the tab should be visible or not.
	 */
	public void setVisible(TabInfo tab, boolean visible) {
		for (int i = 0; i < tabs.size(); ++i) {
			TabInfo info = (TabInfo) tabs.get(i);
			if (info.equals(tab)) {
				Widget w = list.getWidget(i + 1);
				w.setVisible(visible);
				return;
			}
		}
	}
}
